// MsnCoreHelper.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include "MsnCoreHelper.h"
//#include "..\..\My Projects\msnfanatictest\DirectUI.h"
#include "StringTree.h"
#include "process.h"
#include "DUICompat.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//
//	Note!
//
//		If this DLL is dynamically linked against the MFC
//		DLLs, any functions exported from this DLL which
//		call into MFC must have the AFX_MANAGE_STATE macro
//		added at the very beginning of the function.
//
//		For example:
//
//		extern "C" BOOL PASCAL EXPORT ExportedFunction()
//		{
//			AFX_MANAGE_STATE(AfxGetStaticModuleState());
//			// normal function body here
//		}
//
//		It is very important that this macro appear in each
//		function, prior to any calls into MFC.  This means that
//		it must appear as the first statement within the 
//		function, even before any object variable declarations
//		as their constructors may generate calls into the MFC
//		DLL.
//
//		Please see MFC Technical Notes 33 and 58 for additional
//		details.
//

/////////////////////////////////////////////////////////////////////////////
// CMsnCoreHelperApp

BEGIN_MESSAGE_MAP(CMsnCoreHelperApp, CWinApp)
	//{{AFX_MSG_MAP(CMsnCoreHelperApp)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMsnCoreHelperApp construction

CMsnCoreHelperApp::CMsnCoreHelperApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CMsnCoreHelperApp object

CMsnCoreHelperApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CMsnCoreHelperApp initialization
HANDLE hMap =NULL;
BYTE * file_map=NULL;
HWND g_hTargetWnd = 0;
UINT g_nDoIt = RegisterWindowMessage("WM_DUI_DUMP_DO_IT");
//using namespace DirectUI;

void myInvalidParameterHandler(const wchar_t* expression,
   const wchar_t* function, 
   const wchar_t* file, 
   unsigned int line, 
   uintptr_t pReserved)
{
   //wprintf(L"Invalid parameter detected in function %s."
   //         L" File: %s Line: %d\n", function, file, line);
   //wprintf(L"Expression: %s\n", expression);
}

CString VariantToString(VARIANT * var)
{
	try
	{
		VARTYPE vt = var->vt;
		vt &= ~VT_BYREF;

		CString ret;
		switch (vt)
		{
		case VT_EMPTY:
			return "{Empty}";
		case VT_NULL:
			return "{NULL}";
		case VT_I2:
		case VT_I4:
		case VT_I8:
		case VT_UI1:
		case VT_UI2:
		case VT_UI4:
		case VT_UI8:
		case VT_INT:
		case VT_UINT:
			ret.Format("%d",var->lVal);
			return ret;
		case VT_R4:
		case VT_R8:
			ret.Format("%f",var->fltVal);
			return ret;
		case VT_CY:
			return "CY";
		case VT_DATE:
			return "Date";
		case VT_BSTR:
			{
				ret = "Failed BSTR";
				_invalid_parameter_handler oldHandler;

				oldHandler = _set_invalid_parameter_handler(myInvalidParameterHandler);
				_CrtSetReportMode(_CRT_ASSERT, 0);
				ret.Format("%S",((var->vt&VT_BYREF)?*var->pbstrVal:var->bstrVal));
				_set_invalid_parameter_handler(oldHandler);
			}
			return ret;
		case VT_DISPATCH: 
			{
				ret = "pDispatch";			
				return ret;
			}
		case VT_ERROR:
			return "error";
		case VT_BOOL:
			return (var->boolVal?"TRUE":"FALSE");
		case VT_VARIANT:
			return VariantToString(var->pvarVal);
		case VT_UNKNOWN:
			return "Unknown";
		default:
			ret.Format("Other:%d",var->vt);
			return ret;
		}
	}
	catch (CException * ex)
	{
		ex->Delete();
	}
	catch (...)
	{
		
	}
	return "Exception Caught";
}

struct GETRECT_DATA
{
	HANDLE hEvent;
	void * element;
	LPRECT rect;
};
void FillElementText(CDUICompat & g_compat,void * element,IClassInfo * Class, CStringTree * item)
{	
	CString tooltip="";
	void * prop;
	DWORD index = 0;

	// Enumerate Properties
	while (prop=Class->EnumPropertyInfo(index))
	{
		Value * val = g_compat.ElementGetValue(element,prop,2);
		if (val==NULL)
		{
			tooltip+=CString((WCHAR*)(prop))+": <Exception Caught>\r\n";
			continue;
		}

		// This buffer is possibly too small
		WCHAR * wstr = new WCHAR[200];
		ZeroMemory(wstr,200);
		
		CString szPropertyName((WCHAR*)prop);
		if (szPropertyName.CompareNoCase("ID")==0)
		{
			Value* pValue = (Value*)val;
			GetAtomNameW((ATOM)g_compat.GetValueValue(pValue),wstr,199);
		}
		else if (!g_compat.ValueToString(val,wstr, 199))
		{
			wcscpy_s(wstr,200,L"Error");
		}
		
		tooltip+=CString((WCHAR*)prop)+": "+CString(wstr)+"\r\n";
		delete wstr;

		g_compat.ReleaseValue(val);
		index++;
	}

	// Append properties to item data
	item->m_sToolTip+=tooltip;

	// Get the Elements physical rect.
	g_compat.GetElementRect(element,&item->m_rcLocation);
	/*HANDLE hEvent = CreateEvent(0,TRUE,FALSE,0);
	if (hEvent)
	{
		GETRECT_DATA * pData = new GETRECT_DATA;
		pData->hEvent = hEvent;
		pData->element = element;
		pData->rect = &item->m_rcLocation;
		PostMessage(g_hTargetWnd,g_nGetGadgetRectMsg,0,(LPARAM)pData);

		DWORD dw = WaitForSingleObject(hEvent,1000); // Wait this long if it hangs, give up.
		CloseHandle(hEvent);
	}
	else
	{
		ZeroMemory(&item->m_rcLocation,sizeof(RECT));
	}*/
	

	// Attempt to read element Data context
	Value * pValue;
	IUnknown * pUnknown = g_compat.ElementGetDataContext(element,&pValue);
	/*if (pValue && g_compat.GetValueType(pValue) == Value::TYPE_UNKNOWN && pValue->unknownVal)
	{
		IPropertyBag2 * bag = 0;

		if (pUnknown)
		{
			HRESULT hr= pUnknown->QueryInterface(IID_IPropertyBag2,(LPVOID*)&bag);
			if (SUCCEEDED(hr))
			{
				item->m_sToolTip+="-------\r\nData Contexts\r\n-------\r\n";
				ULONG count = 0;
				bag->CountProperties(&count);
				for (ULONG u = 0; u<count; u++)
				{
					ULONG utemp = 0;
					PROPBAG2 bag2;
					bag->GetPropertyInfo(u,1,&bag2,&utemp);
					if (utemp >=1)
					{
						VARIANT v;
						VariantInit(&v);
						
						if (SUCCEEDED(bag->Read(1,&bag2,0,&v,&hr)) && hr == 0)
						{
							CString szName(bag2.pstrName);
							CString szValue =VariantToString(&v);

							item->m_sToolTip+=szName+": "+szValue+"\r\n";
							VariantClear(&v);
						}
					}

				}
				bag->Release();
			}
		}
		g_compat.ReleaseValue(pValue);
	}*/
}

void AddChildren(CDUICompat & g_compat,void * element, CStringTree * tree)
{
	if (element==NULL)
	{
		tree->Add("Um... why is element null?",tree);
		return;
	}
	CString str;
	CString cls="";

	// Get "Children" value
	Value * children = g_compat.ElementGetValue(element,g_compat.GetChildrenProp(),1);

	// Get "ID" value
	Value * id = g_compat.ElementGetValue(element,g_compat.GetIDProp(),1);
	if (id && g_compat.GetValueType(id)==g_compat.TYPE_ATOM())
		str.ReleaseBuffer(GetAtomName((ATOM)g_compat.GetValueValue(id),str.GetBuffer(100),100));

	// Get Class name
	IClassInfo * clsinfo = g_compat.ElementGetClassInfo(element);
	if (clsinfo)
		cls = clsinfo->GetName();

	// Get "Class" Value
	Value * clss = g_compat.ElementGetValue(element,g_compat.GetClassProp(),1);

	if (clss!=NULL && g_compat.GetValueType(clss)==g_compat.TYPE_STRING() && CString((WCHAR*)g_compat.GetValueValue(clss))!="")
		cls += " ("+CString((WCHAR*)g_compat.GetValueValue(clss))+") ";
	if (cls=="")
		cls="Element";
	if (cls=="Element" && str!="")
		cls=str;
	else
		if (str!="")
			cls+="("+str+")";

	str.Format("%x - %s",element,cls);

	// Get "Content" value
	Value * content= g_compat.ElementGetValue(element,g_compat.GetContentProp(),1);
	if (content!=NULL && g_compat.GetValueType(content)==g_compat.TYPE_STRING())
		str+=" - "+CString((WCHAR*)g_compat.GetValueValue(content));

	// Add Item to string tree
	CStringTree* childtree = tree->Add(str,tree);
	
	if (children == NULL)
	{
		childtree->Add("Error: NULL ChildrenProp",childtree);
		return;
	}

	// Retrieve Properties
	FillElementText(g_compat, element, clsinfo,childtree);

	// Enumerate the children
	if (g_compat.GetValueType(children)==g_compat.TYPE_ELEMENTLIST())
	{
		DynamicArray * elementListVal = (DynamicArray*)g_compat.GetValueValue(children);
		if (elementListVal==NULL)
			return; // No children

		for (DWORD i = 0; i<elementListVal->m_dwCount; i++)
		{
			AddChildren(g_compat, elementListVal->m_pElements[i],childtree);
		}	
	}
	else if (g_compat.GetValueType(children)==65535)
	{
		return; // No children
	}
	else
	{
		CString type;
		type.Format("%d",g_compat.GetValueType(children));
		childtree->Add("Child Value "+type,childtree);
	}
}

BOOL loaded = FALSE;
HMODULE msncore=NULL; 

WNDPROC g_pBackupWndProc = 0;

WNDPROC GetOldWindowProc()
{
	if (HIWORD(g_pBackupWndProc)==0xFFFF)
	{
		WORD atom = LOWORD(g_pBackupWndProc);

	}
	return g_pBackupWndProc;
}
LRESULT CALLBACK MyHijackedWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	if (nMsg == g_nGetGadgetRectMsg)
	{
		GETRECT_DATA * pData = (GETRECT_DATA*)lParam;

		//g_compat.GetElementRect(pData->element,pData->rect);
		
		SetEvent(pData->hEvent);

		delete pData;
		return TRUE;
	}
	else if (nMsg==g_nDoIt)
	{
		return theApp.DoIt(*(CStringTree*)lParam);
	}

	return ::CallWindowProc(g_pBackupWndProc,hWnd,nMsg,wParam,lParam);
}
CStringTree g_sTree;

void MyThread(void*);
BOOL CMsnCoreHelperApp::InitInstance()
{
	msncore = ::GetModuleHandle("msncore.dll");

	//g_compat.InitThread();

	HWND temp = ::FindWindow(NULL,"MsnCoreViewer");
	HWND listbox= ::GetDlgItem(temp,1003);
	CListBox list;
	list.Attach(listbox);
	
	CString str;
	list.GetText(list.GetCurSel(),str);
	CString windowtitle = str;//.Right(str.GetLength()-str.Find(" "));
	str = str.Left(str.Find(" "));
	HWND hwnd;
	sscanf_s(str,"%x",&hwnd);
	
	if (IsWindow(hwnd))
	{
		g_hTargetWnd = hwnd;
		if (IsDebuggerPresent())
			DebugBreak();
		g_pBackupWndProc = (WNDPROC)((IsWindowUnicode(hwnd))?GetWindowLongW(hwnd,GWL_WNDPROC):GetWindowLongA(hwnd,GWL_WNDPROC));
		LONG l;
		if (IsWindowUnicode(hwnd))
			l= SetWindowLongW(hwnd,GWL_WNDPROC,(LONG)MyHijackedWndProc);
		else
			l= SetWindowLongA(hwnd,GWL_WNDPROC,(LONG)MyHijackedWndProc);
	}

	list.Detach();

	g_sTree.m_sName=windowtitle;
	g_sTree.m_tParent=NULL;

	PostMessage(hwnd,g_nDoIt,0,(LPARAM)&g_sTree);

	return TRUE;
}

BOOL CMsnCoreHelperApp::DoIt(CStringTree & sTree)
{
	HWND temp = ::FindWindow(NULL,"MsnCoreViewer");
	HWND listbox= ::GetDlgItem(temp,1003);
	CListBox list;
	list.Attach(listbox);

	
	CString str;
	list.GetText(list.GetCurSel(),str);
	CString windowtitle = str.Right(str.GetLength()-str.Find(" "));
	str = str.Left(str.Find(" "));
	HWND hwnd;
	sscanf_s(str,"%x",&hwnd);
	list.Detach();//*/
	CDUICompat g_compat(hwnd);

	void* host = (void*) GetWindowLong(hwnd,GWL_USERDATA);
	void * element = g_compat.GetHwndElement(host);
	AddChildren(g_compat,element,&sTree);

	// Do tree generation here
	CMemFile file;
	CArchive ar(&file,CArchive::store);
	sTree.Serialize(ar);
	sTree.Destroy();
	ar.Close();
	DWORD len = (DWORD)file.GetLength();

	hMap = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,len+4,"MsnCoreViewerMemory");
	if (hMap)																
	{
		file_map =(BYTE*) MapViewOfFile(hMap,FILE_MAP_WRITE,0,0,0);
		if (file_map)
		{
			memcpy(file_map,&len,sizeof(DWORD));
			file.SeekToBegin();
			file.Read(file_map+sizeof(DWORD),len);
		}
	}
	SendMessage(temp,WM_USER+1,0,(LPARAM)hMap);

	file.Close();
	if (IsWindowUnicode(hwnd))
		SetWindowLongW(hwnd,GWL_WNDPROC,(LONG)g_pBackupWndProc);
	else
		SetWindowLongA(hwnd,GWL_WNDPROC,(LONG)g_pBackupWndProc);
	
	HANDLE hJobDone = OpenEvent(EVENT_MODIFY_STATE,FALSE,"MSNCoreViewerJobDone");
	if (hJobDone)
	{
		SetEvent(hJobDone);
		CloseHandle(hJobDone);
	}

	return TRUE;
}

int CMsnCoreHelperApp::ExitInstance() 
{
	if (file_map)
		UnmapViewOfFile(file_map);
	if (hMap)
		CloseHandle(hMap);

	return CWinApp::ExitInstance();
}


